#include "viml/scope_manager.h"
#include <cstdlib>

namespace viml {

ScopeManager::ScopeManager() {
    init_vim_variables();
}

void ScopeManager::init_vim_variables() {
    // 初始化Vim内置变量
    vim_variables_["true"] = Value::boolean(true);
    vim_variables_["false"] = Value::boolean(false);
    vim_variables_["null"] = Value::nil();
    vim_variables_["none"] = Value::none();
    
    // 类型常量
    vim_variables_["t_number"] = Value::make_number(0);
    vim_variables_["t_string"] = Value::make_number(1);
    vim_variables_["t_function"] = Value::make_number(2);
    vim_variables_["t_list"] = Value::make_number(3);
    vim_variables_["t_dict"] = Value::make_number(4);
    vim_variables_["t_float"] = Value::make_number(5);
    vim_variables_["t_bool"] = Value::make_number(6);
}

Value ScopeManager::load(ScopeId scope, const std::string& name) {
    switch (scope) {
        case ScopeId::GLOBAL: {
            auto it = global_scope_.find(name);
            if (it != global_scope_.end()) {
                return it->second;
            }
            throw ScopeException("Undefined global variable: " + name);
        }
        
        case ScopeId::LOCAL: {
            if (function_stack_.empty()) {
                throw ScopeException("Local variable access outside function: " + name);
            }
            return function_stack_.back().get_local(name);
        }
        
        case ScopeId::SCRIPT: {
            if (current_script_.empty()) {
                throw ScopeException("Script variable access without script context: " + name);
            }
            auto script_it = script_scopes_.find(current_script_);
            if (script_it != script_scopes_.end()) {
                auto var_it = script_it->second.find(name);
                if (var_it != script_it->second.end()) {
                    return var_it->second;
                }
            }
            throw ScopeException("Undefined script variable: " + name);
        }
        
        case ScopeId::ARG: {
            if (function_stack_.empty()) {
                throw ScopeException("Argument access outside function: " + name);
            }
            return function_stack_.back().get_argument(name);
        }
        
        case ScopeId::VIM: {
            auto it = vim_variables_.find(name);
            if (it != vim_variables_.end()) {
                return it->second;
            }
            throw ScopeException("Undefined vim variable: v:" + name);
        }
        
        case ScopeId::ENV: {
            // 环境变量访问（只读）
            const char* env_value = std::getenv(name.c_str());
            if (env_value) {
                return Value::string(env_value);
            }
            return Value::nil();
        }
        
        default:
            throw ScopeException("Unknown scope id");
    }
}

void ScopeManager::store(ScopeId scope, const std::string& name, const Value& value) {
    switch (scope) {
        case ScopeId::GLOBAL:
            global_scope_[name] = value;
            break;
        
        case ScopeId::LOCAL: {
            if (function_stack_.empty()) {
                throw ScopeException("Local variable assignment outside function: " + name);
            }
            function_stack_.back().set_local(name, value);
            break;
        }
        
        case ScopeId::SCRIPT: {
            if (current_script_.empty()) {
                throw ScopeException("Script variable assignment without script context: " + name);
            }
            script_scopes_[current_script_][name] = value;
            break;
        }
        
        case ScopeId::ARG:
            throw ScopeException("Cannot assign to function argument: " + name);
        
        case ScopeId::VIM:
            throw ScopeException("Cannot assign to vim variable: v:" + name);
        
        case ScopeId::ENV:
            throw ScopeException("Cannot assign to environment variable: $" + name);
        
        default:
            throw ScopeException("Unknown scope id");
    }
}

bool ScopeManager::exists(ScopeId scope, const std::string& name) const {
    switch (scope) {
        case ScopeId::GLOBAL:
            return global_scope_.find(name) != global_scope_.end();
        
        case ScopeId::LOCAL:
            if (function_stack_.empty()) return false;
            return function_stack_.back().has_local(name);
        
        case ScopeId::SCRIPT: {
            if (current_script_.empty()) return false;
            auto script_it = script_scopes_.find(current_script_);
            if (script_it != script_scopes_.end()) {
                return script_it->second.find(name) != script_it->second.end();
            }
            return false;
        }
        
        case ScopeId::ARG:
            if (function_stack_.empty()) return false;
            return function_stack_.back().has_argument(name);
        
        case ScopeId::VIM:
            return vim_variables_.find(name) != vim_variables_.end();
        
        case ScopeId::ENV:
            return std::getenv(name.c_str()) != nullptr;
        
        default:
            return false;
    }
}

void ScopeManager::push_function_scope(const std::string& func_name,
                                      const std::vector<std::string>& params,
                                      const std::vector<Value>& args,
                                      size_t return_pc,
                                      uint8_t return_reg) {
    function_stack_.emplace_back(func_name, params, args, return_pc, return_reg);
}

void ScopeManager::pop_function_scope() {
    if (function_stack_.empty()) {
        throw ScopeException("Cannot pop function scope: stack is empty");
    }
    function_stack_.pop_back();
}

void ScopeManager::enter_script(const std::string& script_name) {
    current_script_ = script_name;
    // 如果脚本还没有作用域，创建一个空的
    if (script_scopes_.find(script_name) == script_scopes_.end()) {
        script_scopes_[script_name] = {};
    }
}

void ScopeManager::leave_script() {
    current_script_.clear();
}

void ScopeManager::reset() {
    global_scope_.clear();
    script_scopes_.clear();
    function_stack_.clear();
    current_script_.clear();
    init_vim_variables();
}

} // namespace viml
